from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from user import models  # 直接在当前文件夹下导入models
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
from settings import dev
from django.core.cache import cache
from django.conf import settings
import re


class LoginSerializer(serializers.ModelSerializer):
    username = serializers.CharField()

    # 重写username,因为post请求序列化器会自认为是保存操作,然后username设置了唯一,会直接拒绝。
    # 重写之后就post就不再是作为保存的操作，取消了username的unique,可以作为登录验证的操作。
    class Meta:
        model = models.User
        fields = ['username', 'password', 'icon']
        extra_kwargs = {'password': {'write_only': True},
                        }

    def _get_user(self, attrs):

        username = attrs.get('username')
        password = attrs.get('password')

        # 如果是手机号
        if re.match('^1[3-9]\d{9}$', username):
            # 以手机号登录
            user = models.User.objects.filter(mobile=username).first()
        elif re.match('^.+@.+$', username):
            # 以邮箱登录
            user = models.User.objects.filter(email=username).first()
        else:
            # 以用户名登录
            user = models.User.objects.filter(username=username).first()
        if not user:
            raise ValidationError({'username': 'username error'})

        if not user.check_password(password):
            raise ValidationError({'password': 'password error'})

        return user

    def _get_token(self, user):
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

    def validate(self, attrs):
        # 多方式得到user
        user = self._get_user(attrs)

        # user签发token
        token = self._get_token(user)

        # token经过context属性携带给视图类
        self.context['token'] = token

        # 将用户的信息逐个处理传给视图
        self.context['username'] = user.username

        # 通过请求头得到用户的头像地址
        request = self.context['request']
        icon = 'http://%s%s%s' % (request.META['HTTP_HOST'], dev.MEDIA_URL, user.icon)
        self.context['icon'] = icon

        # 将登陆的用户对象传给视图
        self.context['user'] = user

        return attrs


class MobileLoginSerializer(serializers.ModelSerializer):
    # 重写mobile，作为验证操作，而不是保存操作。
    mobile = serializers.CharField(required=True, write_only=True)

    # 因为数据库操作需要pop掉，所以不能序列化出去，不然该定义字段没有值会报错，所以只写。
    code = serializers.CharField(write_only=True)

    class Meta:
        model = models.User
        fields = ['icon', 'mobile', 'code']
        extra_kwargs = {
            'icon': {
                'read_only': True,
            }
        }

    # 手机号格式验证
    def validate_mobile(self, value):
        if not re.match(r'^1[3-9][0-9]{9}$', value):
            raise ValidationError('手机验证错误')
        return value

    def _check_code(self, attrs):
        mobile = attrs.get('mobile')
        # 输入的验证码
        code = attrs.pop('code')
        # 生成的验证码且被缓存
        old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
        if code == old_code or code == '123':
            # 验证码的时效性：一旦验证码验证通过，代表改验证码已使用，需要立即失效
            cache.delete(settings.SMS_CACHE_KEY % {'mobile': mobile})


        else:
            raise ValidationError('输入的验证码错误')

        return mobile

    def _get_user(self, mobile):
        try:
            return models.User.objects.get(mobile=mobile)
        except:
            raise ValidationError('该用户不存在')

    def _get_token(self, user):
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

    def validate(self, attrs):
        # 验证码校验，得到校验通过后的手机号
        mobile = self._check_code(attrs)

        # 通过手机号获取当前用户对象
        user = self._get_user(mobile)

        # 通过用户对象签发token
        token = self._get_token(user)

        # 通过context属性携带给视图类
        self.context['user'] = user
        self.context['token'] = token

        request = self.context['request']
        icon = 'http://%s%s%s' % (request.META['HTTP_HOST'], dev.MEDIA_URL, user.icon)
        self.context['icon'] = icon

        return attrs


class SMSRegisterSerializer(serializers.ModelSerializer):
    # 添加code字段
    code = serializers.CharField(write_only=True)

    class Meta:
        model = models.User
        fields = ['mobile', 'password','code']
        extra_kwargs = {
            'password': {
                'write_only': True
            }
        }

    def validate_mobile(self, value):
        if not re.match(r'^1[3-9][0-9]{9}$', value):
            raise ValidationError('输入的号码格式有误')
        return value

    def validate(self, attrs):
        # 得到验证码校验成功后的mobile
        mobile = attrs.get('mobile')
        code = attrs.pop('code')
        old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})

        if code == old_code or code == '123':
            # 注册入数据库必须要有username字段，所以校验成功后，username默认为手机号
            attrs['username'] = mobile
            # 验证码的时效性：一旦验证码验证通过，代表改验证码已使用，需要立即失效
            cache.delete(settings.SMS_CACHE_KEY % {'mobile': mobile})
            return attrs

        else:
            raise ValidationError('验证码有误')

    # 重写create方法

    def create(self, validated_data):  # 入库的数据应该有：mobile,password,username,  code已被弹出可以无视
        return models.User.objects.create_user(**validated_data)
